package com.example.doublecache.service;

import com.example.doublecache.entity.AlgorithmInfo;
import com.example.doublecache.mapper.AlgorithmMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.concurrent.TimeUnit;

/**
 * 算法信息服务类
 * 实现了算法的查询和更新功能，使用延迟双删方案保证数据一致性
 */
@Service
public class AlgorithmService {
    
    @Autowired
    private AlgorithmMapper algorithmMapper;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private CacheManager layeredCacheManager;
    
    @Value("${cache.delete.delay:1000}")
    private long cacheDeleteDelay;
    
    private static final String LOCK_PREFIX = "lock:algorithm:";
    private static final long LOCK_TIMEOUT = 10; // 锁超时时间（秒）
    
    private static final DefaultRedisScript<Long> LOCK_SCRIPT = new DefaultRedisScript<>();
    private static final DefaultRedisScript<Long> UNLOCK_SCRIPT = new DefaultRedisScript<>();
    
    static {
        // 加锁脚本
        LOCK_SCRIPT.setScriptText(
            "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then " +
            "redis.call('expire', KEYS[1], ARGV[2]) " +
            "return 1 " +
            "else " +
            "return 0 " +
            "end"
        );
        LOCK_SCRIPT.setResultType(Long.class);
        
        // 解锁脚本
        UNLOCK_SCRIPT.setScriptText(
            "if redis.call('get', KEYS[1]) == ARGV[1] then " +
            "return redis.call('del', KEYS[1]) " +
            "else " +
            "return 0 " +
            "end"
        );
        UNLOCK_SCRIPT.setResultType(Long.class);
    }
    
    /**
     * 获取算法信息
     * 使用@Cacheable注解实现缓存，优先从缓存获取数据
     * 缓存未命中时从数据库查询并自动缓存结果
     * @param id 算法ID
     * @return 算法信息
     */
    @Cacheable(value = "algorithm", key = "#id", cacheManager = "layeredCacheManager")
    public AlgorithmInfo getAlgorithm(String id) {
        return algorithmMapper.findById(id);
    }
    
    /**
     * 更新算法信息
     * 使用延迟双删方案保证数据一致性：
     * 1. 获取分布式锁
     * 2. 先删除缓存（包括本地缓存和Redis缓存）
     * 3. 更新数据库
     * 4. 延迟一段时间后再次删除缓存
     * 5. 释放分布式锁
     * @param info 算法信息
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateAlgorithm(AlgorithmInfo info) {
        String lockKey = LOCK_PREFIX + info.getId();
        String lockValue = String.valueOf(System.currentTimeMillis());
        
        try {
            // 1. 获取分布式锁
            boolean locked = acquireLock(lockKey, lockValue);
            if (!locked) {
                throw new RuntimeException("获取分布式锁失败");
            }
            
            String cacheKey = info.getId();
            
            // 2. 先删除缓存
            deleteCache(cacheKey);
            
            // 3. 更新数据库
            int updated = algorithmMapper.update(info);
            if (updated == 0) {
                throw new RuntimeException("更新数据库失败");
            }
            
            // 4. 延迟删除缓存（异步执行）
            delayDeleteCache(cacheKey);
            
        } catch (Exception e) {
            // 发生异常时回滚事务
            throw new RuntimeException("更新算法信息失败", e);
        } finally {
            // 5. 释放分布式锁
            releaseLock(lockKey, lockValue);
        }
    }
    
    /**
     * 获取分布式锁
     * @param lockKey 锁的键
     * @param lockValue 锁的值
     * @return 是否获取成功
     */
    private boolean acquireLock(String lockKey, String lockValue) {
        Long result = redisTemplate.execute(
            LOCK_SCRIPT,
            Collections.singletonList(lockKey),
            lockValue,
            String.valueOf(LOCK_TIMEOUT)
        );
        return result != null && result == 1;
    }
    
    /**
     * 释放分布式锁
     * @param lockKey 锁的键
     * @param lockValue 锁的值
     */
    private void releaseLock(String lockKey, String lockValue) {
        redisTemplate.execute(
            UNLOCK_SCRIPT,
            Collections.singletonList(lockKey),
            lockValue
        );
    }
    
    /**
     * 删除缓存
     * 同时删除本地缓存和Redis缓存
     * @param cacheKey 缓存键
     */
    private void deleteCache(String cacheKey) {
        try {
            // 删除本地缓存
            Cache cache = layeredCacheManager.getCache("algorithm");
            if (cache != null) {
                cache.evict(cacheKey);
            }
            
            // 删除Redis缓存
            String redisKey = "algorithm::" + cacheKey;
            redisTemplate.delete(redisKey);
        } catch (Exception e) {
            throw new RuntimeException("删除缓存失败", e);
        }
    }
    
    /**
     * 延迟删除缓存
     * 使用@Async注解实现异步执行
     * @param cacheKey 缓存键
     */
    @Async
    public void delayDeleteCache(String cacheKey) {
        try {
            // 延迟指定时间后再次删除缓存
            TimeUnit.MILLISECONDS.sleep(cacheDeleteDelay);
            deleteCache(cacheKey);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("延迟删除缓存被中断", e);
        }
    }
} 